package com.itheima.leetcode.od.b.greedy;

import java.util.*;
import java.util.stream.Collectors;

/**
 * (C卷,100分)- 生成哈夫曼树（Java & JS & Python & C）
 * <p>
 * 题目描述
 * <p>
 * 给定长度为 n 的无序的数字数组，每个数字代表二叉树的叶子节点的权值，数字数组的值均大于等于1。
 * <p>
 * 请完成一个函数，根据输入的数字数组，生成哈夫曼树，并将哈夫曼树按照中序遍历输出。
 * <p>
 * 为了保证输出的二叉树中序遍历结果统一，增加以下限制：
 * <p>
 * 二叉树节点中，左节点权值小于右节点权值，根节点权值为左右节点权值之和。当左右节点权值相同时，左子树高度小于等于右子树高度。
 * <p>
 * 注意：
 * <p>
 * 所有用例保证有效，并能生成哈夫曼树。
 * <p>
 * 提醒：
 * <p>
 * 哈夫曼树又称为最优二叉树，是一种带权路径长度最短的二叉树。
 * <p>
 * 所谓树的带权路径长度，就是树中所有的叶节点的权值乘上其到根节点的路径长度（若根节点为 0 层，叶节点到根节点的路径长度为叶节点的层数）
 * <p>
 * 输入描述
 * <p>
 * 例如：由叶子节点：5 15 40 30 10，生成的最优二叉树如下图所示，该树的最短带权路径长度为：40 * 1 + 30 * 2 + 5 * 4 + 10 * 4 = 205。
 * <p>
 * <p>
 * <p>
 * 输出描述
 * <p>
 * 输出一个哈夫曼树的中序遍历数组，数值间以空格分隔
 * <p>
 * <p>
 * 用例
 * <p>
 * 输入	5
 * <p>
 * 5 15 40 30 10
 * <p>
 * 输出	40 100 30 60 15 30 5 15 10
 * <p>
 * 说明	根据输入，生成哈夫曼树，按照中序遍历返回。所有节点中，左节点权值小于等于右节点权值之和。当左右节点权值相同时，左子树高度小于右子树。结果如上图所示。
 */
public class HuffmanTree {
    public static List<Integer> result = new ArrayList<>();

    public static void main(String[] args) {
        /*Scanner in = new Scanner(System.in);
        int n = Integer.parseInt(in.nextLine());
        int[] nums = split(in.nextLine(), " ");*/

        PriorityQueue<TreeNode> queue = new PriorityQueue<>((n1, n2) -> {
            if (n1.weight == n2.weight) {
                return n1.height - n2.height;
            }
            return n1.weight - n2.weight;
        });

        Arrays.stream("5 15 40 30 10".split(" "))
                .mapToInt(Integer::parseInt)
                .forEach(i -> queue.offer(new TreeNode(null, null, i, 1)));

        while (queue.size() >= 2) {
            TreeNode node1 = queue.poll();
            TreeNode node2 = queue.poll();
            queue.offer(new TreeNode(node1, node2, node1.weight + node2.weight, Math.max(node1.height, node2.height) + 1));
        }

        dfs(queue.poll());

        System.out.println(result.stream().map(String::valueOf).collect(Collectors.joining(" ")));
    }

    public static void dfs(TreeNode node) {
        if (node.left != null) {
            dfs(node.left);
        }
        result.add(node.weight);
        if (node.right != null) {
            dfs(node.right);
        }
    }

    public static class TreeNode {
        private TreeNode left;
        private TreeNode right;
        private int weight;
        private int height;

        public TreeNode(TreeNode left, TreeNode right, int weight, int height) {
            this.left = left;
            this.right = right;
            this.weight = weight;
            this.height = height;
        }
    }

}